﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using NetFrame.Auto;
using NetFrame.Log;

namespace NetFrame
{
    /// <summary>
    /// 用户连接对象信息
    /// </summary>
    public class UserToken
    {
        /// <summary>
        /// 用户连接套接字
        /// </summary>
        public Socket mConn;
        /// <summary>
        /// 用户异步接受网络数据对象
        /// </summary>
        public SocketAsyncEventArgs mReceiveSAEA;
        /// <summary>
        /// 用户异步发送网络数据对象
        /// </summary>
        public SocketAsyncEventArgs mSendSAEA;
        /// <summary>
        /// 消息缓存列表
        /// </summary>
        List<byte> mCache = new List<byte>();
        public LengthEncode mLE;
        public LengthDecode mLD;
        public Encode mEncode;
        public Decode mDecode;
        /// <summary>
        /// 是否正在读取
        /// </summary>
        private bool mIsReading = false;
        /// <summary>
        /// 是否正在写入
        /// </summary>
        private bool mIsWriting = false;
        /// <summary>
        /// 写入队列（先进先出）
        /// </summary>
        Queue<byte[]> mWriteQueue = new Queue<byte[]>();

        public delegate void SendProcess(SocketAsyncEventArgs e);

        public SendProcess mSendProcess;

        public delegate void CloseProcess(UserToken token, string errorMsg);

        public CloseProcess mClosePross;
        public AbsHandlerCenter mCenter;
        public UserToken()
        {
            mReceiveSAEA = new SocketAsyncEventArgs();
            mSendSAEA = new SocketAsyncEventArgs();
            mReceiveSAEA.UserToken = this;
            mSendSAEA.UserToken = this;
            //设置接收对象的缓冲区大小
            mReceiveSAEA.SetBuffer(new byte[1024], 0, 1024);
        }

        public void Receive(byte[] msg)
        {
            mCache.AddRange(msg);//将消息写入缓存列表
            if (!mIsReading)
            {
                mIsReading = true;
                OnData();
            }
        }
        /// <summary>
        /// 缓存中有数据处理
        /// </summary>
        void OnData()
        {
            //解码消息存储对象
            byte[] buff = null;
            if (mLD != null)//当粘包解码器存在的时候，进行粘包处理
            {
                buff = mLD(ref mCache);
                if (buff == null)//消息未接收全时，退出数据处理，等待下次消息到达
                {
                    mIsReading = false;
                    return;
                } //说明数据没有处理完
            }
            else
            {
                if (mCache.Count == 0)//缓存区中没有数据，直接跳出数据处理，等待下次消息到达
                {
                    mIsReading = false;
                    return;
                }
                buff = mCache.ToArray();
                mCache.Clear();
            }
            if (mDecode == null)
            {
                Logger.Error("消息解码回调是空的");
            }
            object msg = mDecode(buff);//进行消息反序列化
            //TODO 通知应用层有消息到达
            mCenter.MessageReceive(this, msg);
            OnData();//尾递归，防止在消息处理过程中有其他消息到达
        }


        public void WriteData(byte[] value)
        {
            if (mConn == null)
            {
                mClosePross(this, "调用已经断开连接");
                return;//此链接已断开
            }
            mWriteQueue.Enqueue(value);
            if (!mIsWriting)
            {
                mIsWriting = true;
                OnWrite();
            }
        }

        public void OnWrite()
        {
            //判断发送消息队列是否有消息
            if (mWriteQueue.Count == 0)
            {
                mIsWriting = false;
                return;
            }
            byte[] buff = mWriteQueue.Dequeue();//取出第一条消息
            mSendSAEA.SetBuffer(buff, 0, buff.Length);//设置消息一步发送对象的数据缓冲区数据
            bool result = mConn.SendAsync(mSendSAEA);//开启异步发送
            if (!result)
            {
                mSendProcess(mSendSAEA);
            }
        }
        public void Writed()
        {
            OnWrite();//与OnData尾递归同理
        }
        public void Close()
        {
            try
            {
                mWriteQueue.Clear();
                mCache.Clear();
                mIsReading = false;
                mIsWriting = false;
                mConn.Shutdown(SocketShutdown.Both);
                mConn.Close();
                mConn = null;
            }
            catch (Exception e)
            {
                Logger.Error(e.Message);
            }

        }
    }
}
